package com.wordpress.music2dot0.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * Represents a group that has a playlist in which clients can participate
 * 
 * @author raf
 */
@XmlRootElement(name = "data")
public class GroupServer {
	/**
	 * The radius of clients in the neighborhood of a group's coordinates
	 */
	private final double RADIUS = 0.5;

	/**
	 * The name of a group
	 */
	@XmlElement
	private String name;

	@XmlElement
	private final List<Connection> members = Collections
			.synchronizedList(new ArrayList<Connection>());

	private final Playlist playlist = Playlist.create(1, "metal");

	private double latitude;

	private double longitude;

	GroupServer() {
		// Constructor to unmarshal to this object
	}

	/**
	 * 
	 * @param name
	 */
	public GroupServer(String name) {
		this.name = name;
		this.latitude = 0;
		this.longitude = 0;
	}

	/**
	 * Creates a group server with a name and coordinates
	 * 
	 * @param name
	 *            the name of the new group
	 * @param latitude
	 *            the latitude of the group
	 * @param longitude
	 *            the longitude of the group
	 */
	public GroupServer(String name, double latitude, double longitude) {
		this.name = name;
		this.latitude = latitude;
		this.longitude = longitude;
	}

	/**
	 * Adds a member who wants to join us
	 * 
	 * @param conn
	 */
	public void addConnection(Connection conn) {
		synchronized (this.members) {
			this.members.add(conn);
			if (this.members.size() <= 1) {
				playNext();
			}
		}
	}

	/**
	 * Retrieves the name of the group
	 * 
	 * @return the name of the group
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * Determines if the given coordinates are near the server
	 * 
	 * @param x
	 *            the latitude of the place to test
	 * @param y
	 *            the longitude of the place to test
	 * @return whether the place is in the radius of the group
	 */
	public boolean isNear(double x, double y) {
		return Math.abs(this.latitude - x) < this.RADIUS
				&& Math.abs(this.longitude - y) < this.RADIUS;
	}

	/**
	 * Plays the next song and informs the clients of this
	 */
	public void playNext() {
		this.playlist.playNext(this);
		synchronized (this.members) {
			for (final Connection member : this.members) {
				try {
					member.sendObject(this.playlist);
				} catch (final IOException e) {
					LoginServer.log.throwing("GroupServer", "playNext", e);
					this.members.remove(member);
				}
			}
		}
	}

	/**
	 * Removes a client from the group
	 * 
	 * @param conn
	 *            the client that wishes to disconnect
	 */
	public void removeConnection(Connection conn) {
		LoginServer.log.info("Removing member");
		synchronized (this.members) {
			this.members.remove(conn);
		}
	}

	/**
	 * Vote for a genre
	 * 
	 * @param genre
	 *            the genre to vote for
	 */
	public void vote(String genre) {
		LoginServer.log.info("Received vote for " + genre);
		this.playlist.addGenre(1, genre);
	}
}
